package pl.edu.mimuw.irs.core.model.petrinet;

import java.util.HashSet;
import java.util.Set;

import pl.edu.mimuw.irs.core.exception.InternalException;
import pl.edu.mimuw.irs.core.exception.InternalException.Cause;
import pl.edu.mimuw.irs.core.util.Position;

/**
 * @author Jakub Rauch
 * Created on: 2009-05-07
 */
public class Transition extends Node {
	//private static Logger logger = Logger.getLogger(Transition.class);
	
	// *** Attributes
	
	// Getters and setters
	
	// *** Methods
	
	public Transition(String id, Position position) {
		setId(id);
		setPosition(position);
	}

	public Transition(Position position) {
		setId(getNewId());
		setPosition(position);
	}

	public Transition(String id) {
		this(id, new Position());
	}

	public Transition() {
		this((String)null);
	}
	
	/**
	 * Transition is active iff all (incoming) arcs are active.
	 */
	public boolean isActive() {
		for (Arc arc : getIncomingArcs()) {
			if (!arc.isActive()) return false;
		}
		return true;
	}

	@Override
	public void addArc(Arc arc) {
		switch (arc.getDirection()) {
			case PLACE_TRANSITION:
				getIncomingArcs().add(arc);
				break;
			case TRANSITION_PLACE:
				getOutgoingArcs().add(arc);
				break;
			default:
				throw new InternalException(Cause.PETRI_NET_UNKNOWN_ARC_DIRECTION);
		}
	}
	
	@Override
	public void removeArc(Arc arc) {
		switch (arc.getDirection()) {
			case PLACE_TRANSITION:
				getIncomingArcs().remove(arc);
				break;
			case TRANSITION_PLACE:
				getOutgoingArcs().remove(arc);
				break;
			default:
				throw new InternalException(Cause.PETRI_NET_UNKNOWN_ARC_DIRECTION);
		}
	}
	
	/**
	 * Executes this transition - it means it will execute all incoming and all 
	 * outgoing arcs.
	 */
	public void invoke() {
		for (Arc incomingArc : getIncomingArcs()) {
			incomingArc.invoke();
		}
		for (Arc outgoingArc : getOutgoingArcs()) {
			outgoingArc.invoke();
		}
	}
	
	/**
	 * Finds transitions, that are beyond the one provided - i.e. that can be activated
	 * by executing this transition. This procedure is useful when it is 
	 * known, that given transition has just been invoked and we need to know which
	 * transitions has been activated (if any).
	 * @param transition
	 * @return
	 */
	public Set<Transition> getFollowingActiveTransitions() {
		Set<Transition> suspectTransitions = new HashSet<Transition>();
		Set<Transition> activatedTransitions = new HashSet<Transition>();

		for (Arc arcTP : getOutgoingArcs()) {
			for (Arc arcPT : arcTP.getPlace().getOutgoingArcs()) {
				suspectTransitions.add(arcPT.getTransition());
			}
		}
		
		for (Transition suspectTransition : suspectTransitions) {
			if (suspectTransition.isActive()) {
				activatedTransitions.add(suspectTransition);
			}
		}
		
		return activatedTransitions;
	}

	public void connect(Place place) {
		getNet().addTranPlaceArc(this.getId(), place.getId());
	}
	
	@Override
	public void destroy() {
		for (Arc incomingArc : getIncomingArcs()) {
			incomingArc.getPlace().removeArc(incomingArc);
			//this.removeArc(incomingArc); TODO - być może usuwać, ale nie koniecznie
		}
		for (Arc outgoingArc : getOutgoingArcs()) {
			outgoingArc.getPlace().removeArc(outgoingArc);
			//this.removeArc(outgoingArc);
		}
	}
}
